#include "trace/smart.hpp"

/**
 * \file
 * \ingroup trace
 * \brief cas::TraceSmart implementation.
 */

namespace cas
{
    SLOG_COMPONENT_DEFINE("TraceSmart");
    static GlobalValue g_blockSize("traceSmart.blockSize", "data block size for TraceSmart", "4k");

    TraceSmart::TraceSmart() : rootTask(MixTask::MixSeries)
    {
        SLOG_FUNCTION_NOARGS();
    }

    TraceSmart::TraceSmart(const std::string &configFile) : rootTask(MixTask::MixSeries)
    {
        SLOG_FUNCTION(configFile);
        if (!configFile.empty())
        {
            loadConfig(configFile);
        }
    }

    TraceSmart::~TraceSmart()
    {
        SLOG_FUNCTION_NOARGS();
    }

    inline SmartIOData TraceSmart::next()
    {
        return rootTask.next();
    }

    void TraceSmart::parseConfig(const toml::value &config)
    {
        SLOG_FUNCTION_NOARGS();
        TraceGenerator::parseConfig(config); // call super

        // automatic update default blockSize
        uint64_t blockSize = convert::dataSizeToSizeType(g_blockSize.GetValue());
        if (config.contains("bs"))
        {
            convert::tomlDataSizeToSizeType(blockSize, config.at("bs"));
            g_blockSize.SetValue(config.at("bs").as_string());
        }

        // get random seed
        const toml::integer seed = toml::find_or<toml::integer>(config, "seed", RNG<>::DefaultSeed);

        // get rootTask array
        const auto tasks = toml::find<toml::array>(config, "tasks");
        size_t n = tasks.size(); // to determine format of logPrefix

        SLOG_INFO("├── bs   = " << blockSize);
        SLOG_INFO("├── seed = " << this->seed);
        SLOG_INFO("└── tasks(" << n << ")");
        SASSERT_MSG(n > 0, "Must config root tasks");

        this->seed = seed;

        std::string type;
        for (const auto &t : tasks)
        {
            // t: each task in toml [[tasks]]
            const auto typeExpect = toml::expect<std::string>(t.at("type"));
            if (!typeExpect.is_ok())
            {
                SFATAL_ERROR_CONT(typeExpect.unwrap_err());
                continue;
            }
            type = typeExpect.unwrap();

            n--;
            SLOG_INFO("    " << (n ? "├" : "└") << "── type = " << type << " #" << (tasks.size() - n - 1));
            std::string logPrefix = std::string("    ") + (n ? "│" : " ") + "   ";

            if ("block" == type)
            {
                auto ptr = BlockTask::makeFromToml(t, logPrefix);
                rootTask.pushBack(ptr);
            }
            else if ("mix" == type)
            {
                auto ptr = MixTask::makeFromToml(t, logPrefix);
                rootTask.pushBack(ptr);
            }
            else if ("random" == type)
            {
                auto ptr = RandomTask::makeFromToml(t, logPrefix);
                rootTask.pushBack(ptr);
            }
            else
            {
                SFATAL_ERROR("type not support " << type);
            }
        }
        rootTask.pushEnd();
    }

    void TraceSmart::dump()
    {
        SLOG_FUNCTION_NOARGS();
        SmartIOData r;
        std::cout << "[dump]" << title << std::endl
                  << "lba,size,rw" << std::endl;
        do
        {
            r = rootTask.next();
            std::cout << r.lba << ',' << r.size << ',' << (r.rw ? 'w' : 'r') << std::endl;
        } while (!rootTask.isEnd());
        puts("[/dump]");
        return;
    }

    void TraceSmart::dumpSteps(const uint64_t maxSteps)
    {
        SLOG_FUNCTION_NOARGS();
        SmartIOData r;
        uint64_t steps = 0ull;
        std::cout << "[dump]" << title << std::endl
                  << "lba,size,rw" << std::endl;
        do
        {
            r = rootTask.next();
            std::cout << r.lba << ',' << r.size << ',' << (r.rw ? 'w' : 'r')
                      << " | " << getCurrentTaskIndex() << ' ' << rootTask.currTaskSteps << std::endl;
            if (unlikely(++steps > maxSteps))
            {
                break;
            }
        } while (!rootTask.isEnd());
        puts("[/dump]");
        return;
    }
}
